//----------------------------------------------------------------
//	NPythonBind Lib
//
//	Python Type template class
//
//	Author: Neil Fang
//	Date:	March,2008
//----------------------------------------------------------------
#pragma once
#include "NException.h"
#include "NModule.h"
//
// template function to get base type
//
template<typename B> inline PyTypeObject* nGetBaseType(void)
{
	return B::getClass();
}
// specialization for type PyObject, used by NObject class
template<> inline PyTypeObject* nGetBaseType<PyObject>(void)
{
	return NULL;
}

/**
 *	a class base for module management
 */
class NClassBase : public PyTypeObject
{
protected:
	std::vector<PyMethodDef>	m_methodTab;

public:
	NClassBase(void)
	{
		NModule::getInstance()->addExportClass(this);
	}

	void addMethodDef(PyMethodDef pyMethod)
	{
		m_methodTab.push_back(pyMethod);
	}

	// why: can't have virtual function here
};

/**
 *	template class
 *	@param T	class 
 *	@param B	base class of T
*/
template<typename T, typename B>
class NClass : public NClassBase
{
private:
	void buildMethodTable(void)
	{
		T::exportClass();

		PyMethodDef endMethod = {NULL};
		m_methodTab.push_back(endMethod);
	}

public:
	void exportToMoudle(PyObject* m)
	{
		// build method table
		buildMethodTable();

		tp_methods	= &(*m_methodTab.begin());

		//--
		if(PyType_Ready(this) < 0)
			throw NException("class type init failed");

		Py_INCREF(this);
		PyModule_AddObject(m, tp_name, (PyObject*)this);
	}

public:
	NClass(const char* szExportName, const char* szExportDoc="blank doc")
	{
		//--
		// PyObject_HEAD_INIT(NULL)
		_ob_next	= NULL;
		_ob_prev	= NULL;
		ob_refcnt	= 1;
		ob_type		= NULL;
		

		// 
		ob_size		= 0; 
		tp_name		= szExportName;	

		tp_basicsize= sizeof(T);
		tp_itemsize	= 0;

		tp_dealloc	= 0;
		tp_print	= 0;
		tp_getattr	= 0;
		tp_setattr	= 0;
		tp_compare	= 0;
		tp_repr		= 0;
		tp_as_number= 0;
		tp_as_sequence=0;
		tp_as_mapping=0;
		tp_hash		= 0;
		tp_call		= 0;
		tp_str		= 0;
		tp_getattro	= 0;
		tp_setattro = 0;
		tp_as_buffer= 0;
		tp_flags	= Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE;
		tp_doc		= szExportDoc;
		tp_traverse	= 0;
		tp_clear	= 0;
		tp_richcompare =0;
		tp_weaklistoffset = 0;
		tp_iter		= 0;
		tp_iternext = 0;
		
		tp_methods	= NULL;	// init later, see the comment below
		tp_members	= 0;
		tp_getset	= 0;
		tp_base		= nGetBaseType<B>();
		tp_dict		= 0;
		tp_descr_get= 0;
		tp_descr_set= 0;
		tp_dictoffset=0;

		tp_init		= 0;
		tp_alloc	= 0;
		tp_new		= PyType_GenericNew;
		tp_free		= 0;
		tp_is_gc	= 0;
		tp_bases	= 0;
		tp_mro		= 0;
		tp_cache	= 0;
		tp_subclasses=0;
		tp_weaklist	= 0;
		tp_del		= 0;

		//---
		// about the method table:
		// we can't build the class method table here, because the NClass object will be
		// used as static, the NMethod<>::m_func is static too. Init NMethod<>::m_func may
		// failed, due to the static object init order issue -- m_func assigned by the T::exportClass(),
		// then overwrite by the m_func init code.
		//

	}
};
